package org.alexdalton.jira;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.alexdalton.jira.model.JiraComment;
import org.alexdalton.jira.model.JiraFilter;
import org.alexdalton.jira.model.JiraIssue;
import org.alexdalton.jira.model.JiraPriority;
import org.alexdalton.jira.model.JiraProject;
import org.alexdalton.jira.model.JiraResolution;
import org.alexdalton.jira.model.JiraServerInfo;
import org.alexdalton.jira.model.JiraStatus;
import org.alexdalton.jira.model.JiraType;
import org.xmlrpc.android.XMLRPCClient;
import org.xmlrpc.android.XMLRPCException;

import android.app.Application;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Bitmap;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;

public class JiraApp extends Application implements OnSharedPreferenceChangeListener {
    public static String JIRA_URI;
    public static final String RPC_PATH = "/rpc/xmlrpc";
    public static String USER_NAME;
    public static String PASSWORD;

    private final ArrayList<JiraFilter> filters = new ArrayList<JiraFilter>();
    private final HashMap<Integer, JiraType> types = new HashMap<Integer, JiraType>();
    private final HashMap<Integer, JiraStatus> statuses = new HashMap<Integer, JiraStatus>();
    private final HashMap<Integer, JiraPriority> priorities = new HashMap<Integer, JiraPriority>();
    private final HashMap<Integer, JiraResolution> resolutions = new HashMap<Integer, JiraResolution>();
    private final ArrayList<JiraProject> projectList = new ArrayList<JiraProject>();

    XMLRPCClient rpcClient;
    Object loginToken;

    private static JiraApp app;
    LoginListener listener;
    JiraServerInfo serverInfo;
    ArrayList<JiraIssue> issueList;

    public boolean allowAllSSL;

    public Object getLoginToken() {
        return loginToken;
    }

    public void loadFilters() throws Exception {
        Object[] f = (Object[]) rpcClient.call("jira1.getFavouriteFilters", loginToken);
        filters.clear();
        for (int i = 0; i < f.length; i++) {
            Map filter = (Map) f[i];
            filters.add(JiraFilter.fromMap(filter));
        }
    }

    public ArrayList<JiraFilter> getFilters() {
        return filters;
    }

    public void loadResolutions() throws Exception {
        Object[] f = (Object[]) rpcClient.call("jira1.getResolutions", loginToken);
        for (int i = 0; i < f.length; i++) {
            Map status = (Map) f[i];
            JiraResolution j = JiraResolution.fromMap(status);
            resolutions.put(j.getId(), j);
        }
    }

    public void loadStatuses() throws Exception {
        Object[] f = (Object[]) rpcClient.call("jira1.getStatuses", loginToken);
        for (int i = 0; i < f.length; i++) {
            Map status = (Map) f[i];
            JiraStatus j = JiraStatus.fromMap(status);
            statuses.put(j.getId(), j);
        }
    }

    public String getStatusLabel(int status) {
        return statuses.get(status).getName();
    }

    public String getStatusIcon(int status) {
        return statuses.get(status).getIcon();
    }

    public Bitmap getStatusBitmap(int prio) {
        return statuses.get(prio).getBitmap();
    }

    public void loadPriorities() throws Exception {
        Object[] f = (Object[]) rpcClient.call("jira1.getPriorities", loginToken);
        for (int i = 0; i < f.length; i++) {
            Map prio = (Map) f[i];
            JiraPriority j = JiraPriority.fromMap(prio);
            priorities.put(j.getId(), j);
        }
    }

    public String getPriorityLabel(int prio) {
        return priorities.get(prio) == null ? "Unknown" : priorities.get(prio).getName();
    }

    public String getPriorityIcon(int prio) {
        return priorities.get(prio) == null ? "" : priorities.get(prio).getIcon();
    }

    public Bitmap getPriorityBitmap(int prio) {
        return priorities.get(prio).getBitmap();
    }

    public void loadTypes() throws Exception {
        Object[] f = (Object[]) rpcClient.call("jira1.getIssueTypes", loginToken);
        for (int i = 0; i < f.length; i++) {
            Map prio = (Map) f[i];
            JiraType j = JiraType.fromMap(prio);
            types.put(j.getId(), j);
        }
    }

    public String getTypeLabel(int prio) {
        return types.get(prio) == null ? "Unknown" : types.get(prio).getName();
    }

    public String getTypeIcon(int prio) {
        return types.get(prio) == null ? "" : types.get(prio).getIcon();
    }

    public Bitmap getTypeBitmap(int prio) {
        return types.get(prio).getBitmap();
    }

    public void loadProjects() throws Exception {
        Object[] projects = (Object[]) rpcClient.call("jira1.getProjectsNoSchemes", loginToken);

        // Print projects
        for (int i = 0; i < projects.length; i++) {
            Map project = (Map) projects[i];
            projectList.add(JiraProject.fromMap(project));
        }
    }

    public ArrayList<JiraProject> getProjects() {
        return projectList;
    }

    public interface LoginListener {
        public void onLoginComplete();
    }

    public void setOnLoginListener(LoginListener listener) {
        this.listener = listener;
    }

    public void doLogin() throws Exception {
        final Handler h = new Handler();

        new Thread(new Runnable() {
            public void run() {
                if (JIRA_URI != null && JIRA_URI.length() < 1) {
                    return;
                }
                if (USER_NAME != null && USER_NAME.length() < 1) {
                    return;
                }
                if (PASSWORD != null && PASSWORD.length() < 1) {
                    return;
                }
                try {
                    rpcClient = new XMLRPCClient(JIRA_URI + RPC_PATH);
                } catch (final Exception e) {
                    e.printStackTrace();
                    h.post(new Runnable() {
                        public void run() {
                            Toast.makeText(JiraApp.this, "Connection error check your server settings " + e, Toast.LENGTH_LONG).show();
                        }
                    });
                    return;
                }
                try {
                    loginToken = rpcClient.call("jira1.login", USER_NAME, PASSWORD);
                } catch (final Exception e) {
                    e.printStackTrace();
                    h.post(new Runnable() {
                        public void run() {
                            Toast.makeText(JiraApp.this, "Login Failed, check your credential settings " + e, Toast.LENGTH_LONG).show();
                        }
                    });
                }
                try {
                    loadFilters();
                    loadProjects();
                    loadStatuses();
                    loadPriorities();
                    loadResolutions();
                    loadTypes();
                    serverInfo = getServerInfo();
                    if (listener != null)
                        listener.onLoginComplete();
                } catch (final Exception e) {
                    e.printStackTrace();
                    h.post(new Runnable() {
                        public void run() {
                            Toast.makeText(JiraApp.this, "Communication error " + e, Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
        }).start();
    }

    private void loadPreferences(SharedPreferences prefs, boolean startup) {
        JIRA_URI = prefs.getString("hostURL", "");
        USER_NAME = prefs.getString("login", "");
        PASSWORD = prefs.getString("password", "");
        allowAllSSL = prefs.getBoolean("allowallssl", false);
    }

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        app = this;

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        loadPreferences(prefs, true);
        prefs.registerOnSharedPreferenceChangeListener(this);
        try {
            doLogin();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void onTerminate() {
        // TODO Auto-generated method stub
        super.onTerminate();
    }

    public static JiraApp get() {
        return app;
    }

    public void onSharedPreferenceChanged(SharedPreferences arg0, String arg1) {
        // TODO Auto-generated method stub
        loadPreferences(arg0, false);
        if (loginToken == null) {
            try {
                doLogin();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public void createIssue(String project, String priority, String type, String assignee, String reporter, String summary, String description) throws XMLRPCException {
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("summary", summary);
        params.put("project", project);
        params.put("type", type);
        params.put("priority", priority);
        params.put("description", description);
        params.put("assignee", USER_NAME);
        params.put("reporter", USER_NAME);

        HashMap<String, Object> issue = (HashMap<String, Object>) rpcClient.call("jira1.createIssue", loginToken, params);
        Toast.makeText(this, "issue created", Toast.LENGTH_SHORT).show();
        Log.v("test", "issue creation complete...");
    }

    public ArrayList<JiraIssue> loadIssues(String filter) throws XMLRPCException {
        Object[] issues = (Object[]) app.rpcClient.call("jira1.getIssuesFromFilter", app.getLoginToken(), filter);
        issueList = new ArrayList<JiraIssue>();
        for (int i = 0; i < issues.length; i++) {
            Map issue = (Map) issues[i];
            issueList.add(JiraIssue.fromMap(issue));
        }
        return issueList;
    }

    public ArrayList<JiraIssue> searchIssues(String query) throws XMLRPCException {
        Object[] issues = (Object[]) app.rpcClient.call("jira1.getIssuesFromTextSearch", app.getLoginToken(), query);
        issueList = new ArrayList<JiraIssue>();
        for (int i = 0; i < issues.length; i++) {
            Map issue = (Map) issues[i];
            issueList.add(JiraIssue.fromMap(issue));
        }
        return issueList;
    }

    public JiraIssue loadIssue(String key) throws XMLRPCException {
        Map issue = (Map) app.rpcClient.call("jira1.getIssue", app.getLoginToken(), key);
        return JiraIssue.fromMap(issue);
    }

    public ArrayList<JiraComment> getComments(String key) throws XMLRPCException {
        Object[] comments = (Object[]) app.rpcClient.call("jira1.getComments", app.getLoginToken(), key);
        if (comments.length > 0) {
            ArrayList<JiraComment> ret = new ArrayList<JiraComment>();
            for (int i = 0; i < comments.length; i++) {
                Map comment = (Map) comments[i];
                ret.add(JiraComment.fromMap(comment));
            }
            return ret;
        } else
            return null;
    }

    public void addComment(String key, String comment) throws XMLRPCException {
        app.rpcClient.call("jira1.addComment", app.getLoginToken(), key, comment);
    }

    public interface OnCommentAddedListener {
        public void onCommentAdded(boolean success);
    }

    public void addCommentAsync(final String key, final String comment, final OnCommentAddedListener listener) {
        new Thread(new Runnable() {
            public void run() {
                try {
                    addComment(key, comment);
                } catch (XMLRPCException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    listener.onCommentAdded(false);
                    return;
                }
                listener.onCommentAdded(true);
            }
        }).start();
    }

    public ArrayList<JiraComment> getUser(String search) throws XMLRPCException {
        Map user = (Map) app.rpcClient.call("jira1.getUser", app.getLoginToken(), search);
        Log.v("Test", "got User: " + user);
        return null;
    }

    public JiraServerInfo getServerInfo() throws XMLRPCException {
        Map map = (Map) app.rpcClient.call("jira1.getServerInfo", app.getLoginToken());

        JiraServerInfo info = JiraServerInfo.fromMap(map);
        Log.v("test", "Server info : " + map);
        return info;
    }

    public ArrayList<JiraIssue> getIssueList() {
        return issueList;
    }

    public ArrayList<String> getProjectsLabels(ArrayList<String> ids) {
        ArrayList<String> ret = new ArrayList<String>();
        for (int i = 0; i < projectList.size(); i++) {
            if (projectList.get(i) != null) {
                ret.add(projectList.get(i).getName());
                if (ids != null) {
                    ids.add(projectList.get(i).getTag());
                }
            }
        }
        return ret;
    }

    public ArrayList<String> getTypeLabels(ArrayList<Integer> ids) {
        ArrayList<String> ret = new ArrayList<String>();
        for (int i = 0; i < types.size(); i++) {
            if (types.get(i) != null) {
                ret.add(types.get(i).getName());
                if (ids != null) {
                    ids.add(new Integer(types.get(i).getId()));
                }
            }
        }
        return ret;
    }

    public ArrayList<String> getPriorityLabels(ArrayList<Integer> ids) {
        ArrayList<String> ret = new ArrayList<String>();
        for (int i = 0; i < priorities.size(); i++) {
            if (priorities.get(i) != null) {
                ret.add(priorities.get(i).getName());
                if (ids != null) {
                    ids.add(new Integer(priorities.get(i).getId()));
                }
            }
        }
        return ret;
    }

    public ArrayList<String> getStatusesLabels(ArrayList<Integer> ids) {
        ArrayList<String> ret = new ArrayList<String>();
        for (int i = 0; i < statuses.size(); i++) {
            if (statuses.get(i) != null) {
                ret.add(statuses.get(i).getName());
                if (ids != null) {
                    ids.add(new Integer(statuses.get(i).getId()));
                }
            }
        }
        return ret;
    }

    public void doRefresh() {
        try {
            doLogin();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    interface OnIssueListLoadedListener {
        public void onIssueListLoaded(ArrayList<JiraIssue> issueList);
    }

    public void getIssueListAsync(final String filter, final OnIssueListLoadedListener listener) {
        new Thread(new Runnable() {
            public void run() {
                ArrayList<JiraIssue> list;
                try {
                    list = loadIssues(filter);
                    listener.onIssueListLoaded(list);
                } catch (XMLRPCException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public JiraIssue getIssueForKey(String key) {
        JiraIssue ret = null;
        if (issueList == null)
            return null;
        for (int i = 0; i < issueList.size(); i++) {
            if (issueList.get(i).getKey().equals(key)) {
                ret = issueList.get(i);
                break;
            }
        }
        return ret;
    }

    interface OnIssueLoadedListener {
        public void onIssueLoaded(JiraIssue issue);
    }

    public void getIssueAsync(final String key, final boolean loadComments, final OnIssueLoadedListener listener) {
        new Thread(new Runnable() {
            public void run() {
                JiraIssue issue;
                try {
                    issue = loadIssue(key);
                    if (loadComments)
                        issue.setComments(getComments(key));
                    listener.onIssueLoaded(issue);
                } catch (XMLRPCException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    interface OnSearchCompleteListener {
        public void onSearchComplete(ArrayList<JiraIssue> issues);
    }

    public void searchIssueAsync(final String query, final OnSearchCompleteListener listener) {
        new Thread(new Runnable() {
            public void run() {
                ArrayList<JiraIssue> issues;
                try {
                    issues = searchIssues(query);
                    listener.onSearchComplete(issues);
                } catch (XMLRPCException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void updateIssue(String key, Map<String, Vector> map) {
        try {
            app.rpcClient.call("jira1.updateIssue", app.getLoginToken(), key, map);
        } catch (XMLRPCException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
